/*
 * Copyright (C) 2006-2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;

using Gtk;
using Mono.Unix;

using DCSharp.Backend.Connections;
using DCSharp.Backend.Managers;
using DCSharp.Backend.Objects;
using DCSharp.Xml;

namespace DCSharp.Gui
{
	public static class Util
	{
		public static string AppName
		{
			get { return "DC#"; }
		}

		public static string AppVersion
		{
			get { return Assembly.GetEntryAssembly().GetName().Version.ToString(); }
		}

		public static string Website
		{
			get { return "http://code.google.com/p/dcsharp/"; }
		}

		public static string GetResource(string name)
		{
#if WINDOWS
			if (name == null)
			{
				throw new ArgumentNullException("name");
			}
			if (name.EndsWith(".glade"))
			{
				return "DCSharp.Gui.resources.glade." + name;
			}
			return "DCSharp.Gui.resources." + name;
#else
			return name;
#endif
		}

		#region Icon

		private static Widget widget = new Invisible();

#if !GNOME
		private static string[][] Extensions = {
			new string[] {"au", "mp2", "mp3", "mid", "ogg", "rm", "sm", "wav"},
			new string[] {"arc", "arj", "gz", "lzh", "pak", "rar", "z", "zip"},
			new string[] {"doc", "pdf", "ps", "tex", "txt", "wri"},
			new string[] {"bat", "com", "exe", "pm"},
			new string[] {"bmp", "gif", "jpg", "jpeg", "pcx", "png", "psd", "wmf"},
			new string[] {"avi", "asf", "mov", "mpeg", "mpg"}
		};
#endif

		private static string[][] icons = {
			new string[] {"audio-x-generic", "gnome-mime-audio"},
			new string[] {"package-x-generic", "gnome-package"},
			new string[] {"text-x-generic", "gtk-file"},
			new string[] {"application-x-executable", "gnome-fs-executable"},
			new string[] {"image-x-generic", "gnome-mime-image"},
			new string[] {"video-x-generic", "gnome-mime-video"},
			new string[] {"folder", "gnome-fs-directory"}
		};

		/*
		public enum SearchFileType
		{
			Any = 1,
			Audio,
			Package,
			Document,
			Executable,
			Image,
			Video,
			Directory
		}
		*/
		public static Gdk.Pixbuf GetFileTypeIcon(SearchFileType type)
		{
			if (type != SearchFileType.Any)
			{
				return GetIcon(icons[(int)type - 2]);
			}

			// FIXME: Need another icon for this one
			return widget.RenderIcon(Stock.About, IconSize.Menu, null);
		}

		public static Gdk.Pixbuf GetIconFromPath(string path)
		{
			return GetIconFromPath(path, 16);
		}

		public static Gdk.Pixbuf GetIconFromPath(string path, int size)
		{
			return IconManager.GetIcon(GetIconName(path), size);
		}

		public static string GetIconName(string fileName)
		{
#if GNOME
			string mimeType = Gnome.Vfs.Mime.TypeFromName(fileName);

			Gnome.IconLookupResultFlags result;
			string iconName = Gnome.Icon.Lookup(IconTheme.Default, null, null,
				null, null, mimeType, Gnome.IconLookupFlags.None, out result);

			// TODO: Replace default icon from Gnome.Icon.Lookup
			return iconName;
#else
			string extension = System.IO.Path.GetExtension(fileName);
			if (extension.Length == 0)
			{
				return "gtk-file";
			}

			int index = 0;
			extension = extension.ToLower().Substring(1);
			foreach (string[] extensions in Extensions)
			{
				if (Array.IndexOf(extensions, extension) > -1)
				{
					foreach (string iconName in icons[index])
					{
						if (IconTheme.Default.HasIcon(iconName))
						{
							return iconName;
						}
					}
				}
				index++;
			}
			return "gtk-file";
#endif
		}

		public static Gdk.Pixbuf GetUserIcon(Identity identity)
		{
			if (identity == null)
			{
				throw new ArgumentNullException("identity");
			}
			if (identity.User.IsOnline)
			{
				if (identity.Op)
				{
					return IconManager.UserOP;
				}
				else if (identity.Active)
				{
					return IconManager.UserOnline;
				}
				return IconManager.UserPassive;
			}
			return IconManager.UserOffline;
		}

		public static Gdk.Pixbuf GetUserIcon(User user)
		{
			if (user == null)
			{
				throw new ArgumentNullException("user");
			}
			return user.IsOnline ? IconManager.UserOnline : IconManager.UserOffline;
		}

		public static Gdk.Pixbuf GetHubIcon(HubConnection hub)
		{
			if (hub == null)
			{
				throw new ArgumentNullException("hub");
			}
			return GetHubIcon(hub.State == ConnectionState.Connected);
		}

		public static Gdk.Pixbuf GetHubIcon(string uriString)
		{
			HubConnection hub;
			return GetHubIcon(Globals.Runtime.HubManager.TryGetHub(uriString, out hub));
		}

		public static Gdk.Pixbuf GetHubIcon(bool connected)
		{
			return connected ? IconManager.HubOnline : IconManager.HubOffline;
		}

		private static Gdk.Pixbuf GetIcon(string[] iconNames)
		{
			return GetIcon(iconNames, 16);
		}

		private static Gdk.Pixbuf GetIcon(string[] iconNames, int size)
		{
			foreach (string iconName in iconNames)
			{
				if (IconTheme.Default.HasIcon(iconName))
				{
					return IconManager.GetIcon(iconName, size);
				}
			}
			return null;
		}

		#endregion

		public static string FormatFileSize(long bytes)
		{
			double size = bytes;
			bool decimals = true;

			int count = 0;
			while (size >= 1024)
			{
				size /= 1024;
				count++;
			}

			string units = null;
			switch(count)
			{
				case 0:
					units = "byte";
					decimals = false;
					break;
				case 1:
					units = "KiB";
					break;
				case 2:
					units = "MiB";
					break;
				case 3:
					units = "GiB";
					break;
				case 4:
					units = "TiB";
					break;
				case 5:
					units = "PiB";
					break;
				case 6:
					units = "EiB";
					break;
				default:
					throw new OverflowException();
			}

			string format = decimals ? "{0:0.0} {1}" : "{0:0} {1}";

			return String.Format(format, size, units);
		}

		public static void OpenFile(string file)
		{
			OpenUrl(file.StartsWith("file.//") ? file : "file://" + file);
		}

		public static void OpenUrl(string url)
		{
			try
			{
				Process.Start(String.Format("\"{0}\"", url));
			}
			catch
			{
				string text = Catalog.GetString("Could not open \"{0}\"");
				text = String.Format(text, url);

				MessageDialog dialog = new MessageDialog(text, null, null,
					MessageType.Error, Globals.Gui.MainWindow.Window);
				dialog.AddButton(Stock.Ok, ResponseType.Ok);

				dialog.Run();
				dialog.Destroy();
			}
		}

		public static string GetStatus(Downloader downloader)
		{
			if (downloader == null)
			{
				throw new ArgumentNullException("downloader");
			}
			switch (downloader.State)
			{
				case DownloadState.Stopped:
					Exception exception = downloader.Exception;
					if (exception != null)
					{
						return String.Format(Catalog.GetString("Error: {0}"),
							exception.Message);
					}
					return Catalog.GetString("Stopped");

				case DownloadState.Loading:
					return Catalog.GetString("Verifying...");

				case DownloadState.Active:
					return Catalog.GetString("Receiving...");

				case DownloadState.Done:
					return Catalog.GetString("Done");

				default:
					FileDownloader fileDownloader = downloader as FileDownloader;
					if (fileDownloader != null)
					{
						return GetIdleStatus(fileDownloader);
					}
					return GetIdleStatus((ListDownloader)downloader);
			}
		}

		private static string GetIdleStatus(FileDownloader downloader)
		{
			int valid = 0;
			int validOnline = 0;
			lock (downloader.UserLock)
			{
				foreach (SourceInfo source in downloader)
				{
					if (source.IsValid)
					{
						valid++;
						if (source.User.IsOnline)
						{
							validOnline++;
						}
					}
				}
			}
			return GetIdleStatus(valid, validOnline);
		}

		private static string GetIdleStatus(ListDownloader downloader)
		{
			return GetIdleStatus(1, downloader.User.IsOnline ? 1 : 0);
		}

		private static string GetIdleStatus(int users, int online)
		{
			if (users == 0)
			{
				return Catalog.GetString("No users to download from");
			}
			else if (online > 0)
			{
				if (online == 1)
				{
					return Catalog.GetString("Waiting (User online)");
				}
				return String.Format(
					Catalog.GetString("Waiting ({0} of {1} users online)"),
					online, users);
			}
			else
			{
				if (users == 1)
				{
					return Catalog.GetString("User offline");
				}
				else if (users == 2)
				{
					return Catalog.GetString("Both users offline");
				}
				return Catalog.GetString("All users offline");
			}
		}

		public static string GetStatus(Uploader uploader)
		{
			if (uploader == null)
			{
				throw new ArgumentNullException("uploader");
			}
			if (uploader.Active)
			{
				return Catalog.GetString("Sending");
			}
			else if (uploader.Progress == 1)
			{
				return Catalog.GetString("Done");
			}
			else
			{
				return Catalog.GetString("Aborted");
			}
		}

		public static string GetHubName(HubConnection connection)
		{
			foreach (FavoriteHubInfo hubInfo in Globals.FavoriteManager.Hubs)
			{
				Uri uri;
				if (hubInfo.Hostname != null &&
					StringUtil.TryCreateUri(hubInfo.Hostname, out uri) &&
					uri == connection.Uri)
				{
					return hubInfo.Name;
				}
			}
			return connection.Uri.ToString();
		}

		public static void SetVisibleColumns(TreeViewColumn[] columns,
			int visibleColumns)
		{
			if (columns == null)
			{
				throw new ArgumentNullException("columns");
			}
			int i = 0;
			foreach (TreeViewColumn column in columns)
			{
				column.Visible = (visibleColumns & (1 << i)) > 0;
				i++;
			}
		}

		public static int GetVisibleColumns(TreeViewColumn[] columns)
		{
			if (columns == null)
			{
				throw new ArgumentNullException("columns");
			}
			int i = 0;
			int visible = 0;
			foreach (TreeViewColumn column in columns)
			{
				visible |= column.Visible ? (1 << i) : 0;
				i++;
			}
			return visible;
		}

		public static void RequestFileList(User user)
		{
			RequestFileList(user, null);
		}

		public static void RequestFileList(User user, string pathToSelect)
		{
			ListDownloader downloader = Globals.Runtime.DownloadManager.DownloadList(user);
			Globals.Gui.HandleListDownload(downloader, pathToSelect);
		}

		private static string localIP;

		public static string GetLocalIPAddress()
		{
			if (localIP == null)
			{
				IPHostEntry iphostentry = Dns.GetHostEntry(Dns.GetHostName());
				localIP = iphostentry.AddressList[0].ToString();
			}
			return localIP;
		}
	}
}
